package com.example.gamblingshow;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Formatter;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SocketServer extends JFrame {
    private Player[] players;
    private ServerSocket server;
    //当前玩家
    private int currentPlayer;
    private ExecutorService runGame;
    private Lock gameLock;
    private Condition otherPlayerConnected;//to wait for other player
    private Condition otherPlayerTurn;//to wait for other player's turn

    private final static int STARTER = 0;

    private final static String[] MARKS = {"X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9", "X10"}; // array of marks

    private JTextArea outputArea;

    private Integer playerNum;

    public SocketServer() {
        super("SocketServer");
        //弹出对话框，提示输入游戏参与人数
        playerNum = Integer.parseInt(JOptionPane.showInputDialog(null, "请输入参与人数(2~10人)"));
        players = new Player[playerNum];
        //创建一个线程池，具有playerNum个线程
        runGame = Executors.newFixedThreadPool(playerNum);
        //新建一个互斥锁，具有lock和unlock方法，表示上锁和解锁
        gameLock = new ReentrantLock();
        //创建gameLock的两个条件
        otherPlayerConnected = gameLock.newCondition();
        otherPlayerTurn = gameLock.newCondition();
        //指定开始玩家为X1
        currentPlayer = STARTER;

        try {
            server = new ServerSocket(12345, playerNum);
        } catch (IOException ioException) {
            ioException.printStackTrace();
            System.exit(1);
        }
        //添加信息输出框
        outputArea = new JTextArea();
        add(outputArea, BorderLayout.CENTER);
        outputArea.setText("Server awaiting connections\n");
        setSize(300, 300);
        setVisible(true);
    }

    public void execute() {
        //等待所有人连接
        for (int i = 0; i < players.length; i++) {
            try {
                players[i] = new Player(server.accept(), i);
                runGame.execute(players[i]);
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }

        gameLock.lock();
        players[STARTER].setSuspend(false);
        otherPlayerConnected.signal();
        gameLock.unlock();
    }

    private void displayMessage(final String messageToDisplay) {
        SwingUtilities.invokeLater(() -> outputArea.append(messageToDisplay));
    }

    //各类奖品数
    // 0:状元 1:榜眼 2:探花 3:进士 4:举人 5:秀才
    Integer[] rewards = {1, 2, 4, 8, 16, 32};
    String[] rewardTypes = {"zhuangyuan", "bangyan", "tanhua", "jinshi", "juren", "xiucai"};

    //是否结束游戏，即奖品发完
    private boolean isGameOver() {
        return Arrays.stream(rewards).reduce(0, Integer::sum) == 0;
    }

    //each player as a runnable
    private class Player implements Runnable {
        private Socket connection;//connection to client
        private int playerNumber;//当前玩家编号
        private String mark;//X Y

        private Scanner input;//client接收信息
        private Formatter output;//给client发送信息

        private boolean suspended = true;//线程是否被悬挂

        private int[] results = new int[7];//传回来的结果
        int[] point = new int[7];//计算每个子的个数

        //得到奖品
        Integer[] myRewards = {0, 0, 0, 0, 0, 0};

        public Player(Socket socket, int number) {
            playerNumber = number;
            mark = MARKS[playerNumber];
            connection = socket;

            try {
                input = new Scanner(connection.getInputStream());
                output = new Formatter(connection.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }

        @Override
        public void run() {
            try {
                displayMessage("Player " + mark + " connected\n");
                output.format("%s\n", mark);
                output.flush();
                if (playerNumber == STARTER) {
                    output.format("Player %s connected\nWaiting for other players\n", mark);
                    output.flush();
                    gameLock.lock();
                    try {
                        while (suspended) {
                            otherPlayerConnected.await();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        gameLock.unlock();
                    }
                    //游戏开始
                    output.format("Other player(s) connected. Your move.\n");
                    output.flush();
                } else {
                    output.format("Player %s connected. Please wait.\n", mark);
                    output.flush();
                }

                while (!isGameOver()) {
                    while (playerNumber != currentPlayer) {//判断当前是否是自己的回合
                        gameLock.lock();
                        try {
                            otherPlayerTurn.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            gameLock.unlock();
                        }
                    }
                    if (input.hasNext()) {
                        String[] re = input.nextLine().split(" ");
                        for (int i = 1; i <= 6; i++) point[i] = 0;
                        for (int i = 0; i < re.length; i++) {
                            results[i] = Integer.parseInt(re[i]);
                            point[results[i]]++;
                        }
                        boolean getAward = judgeZhuangyuan() || judgeBangyan() || judgeTanhua() || judgeJinshi() || judgeJuren() || judgeXiucai();
                        if (!getAward) {
                            output.format("nothing\n");
                            output.flush();
                        }
                    }
                    currentPlayer = (currentPlayer + 1) % playerNum;
                    players[currentPlayer].otherPlayerMoved();
                    gameLock.lock();
                    otherPlayerTurn.signal();
                    gameLock.unlock();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                output.format("Game is over.\n");
                output.flush();
                output.format("最总成绩:\n状元:%d 榜眼:%d 探花:%d 进士:%d 举人:%d 秀才:%d\n",
                        myRewards[0], myRewards[1], myRewards[2], myRewards[3], myRewards[4], myRewards[5]);
                output.flush();
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.exit(1);
                }
            }
        }

        //线程是否被悬挂
        public void setSuspend(boolean status) {
            suspended = status;
        }

        public void otherPlayerMoved() {
            if (isGameOver()) {
                output.format("Game is over.\n");
                output.flush();
                output.format("最终成绩:\n状元:%d 榜眼:%d 探花:%d 进士:%d 举人:%d 秀才:%d\n",
                        myRewards[0], myRewards[1], myRewards[2], myRewards[3], myRewards[4], myRewards[5]);
                output.flush();
            } else {
                output.format("myTurn\n");
                output.flush();
            }
        }

        public void giveReward(int type) {
            //0:状元 1:榜眼 2:探花 3:进士 4:举人 5:秀才
            if (rewards[type] == 0) {
                output.format("%s but no reward\n", rewardTypes[type]);
                output.flush();
            }
            rewards[type]--;
            myRewards[type]++;
            output.format("%s\n",rewardTypes[type]);
            output.flush();
        }

        public boolean judgeZhuangyuan() {
            if (point[4] >= 4 || point[2] >= 5 || point[1] >= 5 || point[3] >= 5 || point[5] >= 5 || point[6] >= 5) {
                giveReward(0);
                return true;
            } else return false;
        }

        public boolean judgeBangyan() {
            if (point[1] == 1 && point[2] == 1 && point[3] == 1 && point[4] == 1 && point[5] == 1 && point[6] == 1) {
                giveReward(1);
                return true;
            } else return false;
        }

        public boolean judgeTanhua() {
            if (point[4] == 3) {
                giveReward(2);
                return true;
            } else return false;
        }

        public boolean judgeJinshi() {
            if (point[1] == 4 || point[2] == 4 || point[3] == 4 || point[5] == 4 || point[6] == 4) {
                giveReward(3);
                return true;
            } else return false;
        }

        public boolean judgeJuren() {
            if (point[4] == 2) {
                giveReward(4);
                return true;
            } else return false;
        }

        public boolean judgeXiucai() {
            if (point[4] == 1) {
                giveReward(5);
                return true;
            } else return false;
        }
    }
}
